﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CPS1
{
    // klasa obliczeń na sygnałach

    //w drugiej części znajduje się część porównująca sygnał analogowy z cyfrowym
    public class SignalOperations
    {
        public static DiscreetSignal operation(Signal sign1, Signal sign2, char op) 
        {
            Double t1 = 0.0;
            Double A = 0.0; //
            int commonRange = 0;


            //w zasadzie to maplituda bedzie inna, to chyba powinna byc maksymalna wartosc po przeprowadzeniu operacji
            if (sign1.A > sign2.A)
                A = sign1.A;
            else if(sign1.A < sign2.A)
                A = sign2.A;
            //



            if (sign1.t1 == sign2.t1)
            {
                // określamy czas początkowy
                t1 = sign1.t1;
                // określamy ilość próbek dla których wykonamy obliczenia - dla pozostałych wykorzystamy próbki z sygnału dłuższego
                commonRange = (sign1.calculatedValues.Length > sign2.calculatedValues.Length) ? sign1.calculatedValues.Length / 2 : sign2.calculatedValues.Length / 2;
                // obliczenia
                Double[,] values = new Double[commonRange, 2];
                Double j = t1;

                for (int i = 0; i < commonRange; i++)
                {
                    values[i, 0] = j;
                    
                    if (op == '+')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) + ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '-')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) - ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '*')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) * ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '/')
                    {
                        if (sign2.calculatedValues[i, 1] == 0)
                        {
                            values[i, 1] = 0.0;
                        }
                        else
                        {
                            values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) / ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                        }
                    }

                    j = Math.Round(j + ((double)1.0 / (double) sign1.f), 6);
                }

                return new DiscreetSignal("", A, t1, sign1.f, values);
            }
            else if (sign1.t1 < sign2.t1) // gdy sygnał pierwszy zaczyna się wcześniej niż drugi
            {
                t1 = sign1.t1;
                // różnica próbek
                int diff = (int) Math.Ceiling((sign2.t1 - sign1.t1) * sign1.f);
                // część wspólna
                commonRange = (sign1.calculatedValues.Length / 2 > (sign2.calculatedValues.Length / 2) + diff) ? sign1.calculatedValues.Length / 2 : (sign2.calculatedValues.Length / 2) + diff;
                // obliczenia
                Double[,] values = new Double[commonRange, 2];
                Double j = t1;

                for (int i = 0; i < commonRange; i++)
                {
                    values[i, 0] = j;

                    if (op == '+')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) +
                                       ((i > diff && i < ((sign2.calculatedValues.Length / 2) + diff)) ? sign2.calculatedValues[i - diff, 1] : 0.0);
                    }
                    else if (op == '-')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) -
                                       ((i > diff && i < ((sign2.calculatedValues.Length / 2) + diff)) ? sign2.calculatedValues[i - diff, 1] : 0.0);
                    }
                    else if (op == '*')
                    {
                        values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) *
                                       ((i > diff && i < ((sign2.calculatedValues.Length / 2) + diff)) ? sign2.calculatedValues[i - diff, 1] : 0.0);
                    }
                    else if (op == '/')
                    {
                        if (i > diff && sign2.calculatedValues[i - diff, 1] == 0)
                        {
                            values[i, 1] = 0.0;
                        }
                        else
                        {
                            values[i, 1] = ((i < (sign1.calculatedValues.Length / 2)) ? sign1.calculatedValues[i, 1] : 0.0) *
                                       ((i > diff && i < ((sign2.calculatedValues.Length / 2) + diff)) ? sign2.calculatedValues[i - diff, 1] : 0.0);
                        }
                    }

                    j = Math.Round(j + ((double)1.0 / (double)sign1.f), 6);
                }

                return new DiscreetSignal("", A, t1, sign1.f, values);
            }
            else // gdy sygnał drugi zaczyna się wcześniej niż pierwszy
            {
                t1 = sign2.t1;
                // różnica próbek
                int diff = (int)Math.Ceiling((sign1.t1 - sign2.t1) * sign1.f);
                // część wspólna
                commonRange = ((sign1.calculatedValues.Length / 2) + diff > sign2.calculatedValues.Length / 2) ? (sign1.calculatedValues.Length / 2) + diff : sign2.calculatedValues.Length / 2;
                // obliczenia
                Double[,] values = new Double[commonRange, 2];
                Double j = t1;

                for (int i = 0; i < commonRange; i++)
                {
                    values[i, 0] = j;
                    
                    if (op == '+')
                    {
                        values[i, 1] = ((i > diff && i < ((sign1.calculatedValues.Length / 2) + diff)) ? sign1.calculatedValues[i - diff, 1] : 0.0) +
                                       ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '-')
                    {
                        values[i, 1] = ((i > diff && i < ((sign1.calculatedValues.Length / 2) + diff)) ? sign1.calculatedValues[i - diff, 1] : 0.0) -
                                       ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '*')
                    {
                        values[i, 1] = ((i > diff && i < ((sign1.calculatedValues.Length / 2) + diff)) ? sign1.calculatedValues[i - diff, 1] : 0.0) *
                                       ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                    }
                    else if (op == '/')
                    {
                        if (sign2.calculatedValues[i, 1] == 0)
                        {
                            values[i, 1] = 0.0;
                        }
                        else
                        {
                            values[i, 1] = ((i > diff && i < ((sign1.calculatedValues.Length / 2) + diff)) ? sign1.calculatedValues[i - diff, 1] : 0.0) *
                                       ((i < (sign2.calculatedValues.Length / 2)) ? sign2.calculatedValues[i, 1] : 0.0);
                        }
                    }

                    j = Math.Round(j + ((double)1.0 / (double)sign1.f), 6);
                }

                return new DiscreetSignal("", A, t1, sign1.f, values);
            }
        }

        /*public static Signal conversionParams(Signal sign1, Signal sign2, string op) {

            //return new Signal();
        }*/


        public static double conversionParams(Signal sign1, Signal sign2, string op)
        {
            if (op == "SNR") {
                double SNR= SNRfun (sign1,sign2);
                return SNR;
            }
            else if (op == "PSNR") { 
                double MSE = MSEfun(sign1,sign2);
                double PSNR= PSNRfun (sign1,sign2, MSE);
                return PSNR;
            }
            else if (op == "MSE") { 
                double MSE= MSEfun (sign1,sign2);
                return MSE;
            }
            else if (op == "MD") { 
                double MD= MDfun (sign1,sign2);
                return MD;
            }

            return 0.0;
        }
        //signal to noise ratio (dB)
        public static double SNRfun (Signal sign1, Signal sign2){
            // określamy ilość próbek dla których wykonamy obliczenia - dla pozostałych wykorzystamy próbki z sygnału dłuższego, zasadniczo powinno ich być tyle samo ale na wszelki wypadek sprawdzamy
            int commonRange;
            double SNR;
            double total=0;
            commonRange = (sign1.calculatedValues.Length > sign2.calculatedValues.Length) ? sign1.calculatedValues.Length / 2 : sign2.calculatedValues.Length / 2;
            for(int i=0; i<commonRange; i++){
                //sign2.calculatedValues[i, 1] nie wiem czy to jest dobrze, nie jestem pewna czy rzeczywiscie tak sie wyciaga ta wartosc
                total+=Math.Pow(sign1.calculatedValues[i, 1],2)/Math.Pow((sign1.calculatedValues[i, 1]-sign2.calculatedValues[i, 1]),2);
            }
            SNR=10*Math.Log10(total);
            return SNR;
                
        }

        //mean squared error
        public static double MSEfun (Signal sign1, Signal sign2){
            int commonRange;
            double MSE;
            double total=0;
            commonRange = (sign1.calculatedValues.Length > sign2.calculatedValues.Length) ? sign1.calculatedValues.Length / 2 : sign2.calculatedValues.Length / 2;
            for(int i=0; i<commonRange; i++){
                //sign2.calculatedValues[i, 1] nie wiem czy to jest dobrze, nie jestem pewna czy rzeczywiscie tak sie wyciaga ta wartosc
                total+=Math.Pow(sign1.calculatedValues[i, 1]-sign1.calculatedValues[i, 1],2);                             
            }
            MSE=1/commonRange*total;
            return MSE;
        }

        //peak signal to nise ratio (dB)
        public static double PSNRfun (Signal sign1, Signal sign2, double MSE){
            double PSNR;
            int commonRange;
            double tmp=0; //nie chce tutaj tego zera ale mi każe
            double tmp1;
            commonRange = (sign1.calculatedValues.Length > sign2.calculatedValues.Length) ? sign1.calculatedValues.Length / 2 : sign2.calculatedValues.Length / 2;
            for(int i=0; i<commonRange-1; i++){
                //sign2.calculatedValues[i, 1] nie wiem czy to jest dobrze, nie jestem pewna czy rzeczywiscie tak sie wyciaga ta wartosc
                tmp=sign1.calculatedValues[i, 1];
                tmp1=sign1.calculatedValues[i+1, 1];
                if(tmp1>tmp){
                    tmp=tmp1;   
                }
            }
            PSNR=10*Math.Log10(tmp/MSE);
            return PSNR;
        }

        //maximum difference
        public static double MDfun (Signal sign1, Signal sign2){
            double MD;
            int commonRange;
            double tmp=0; //nie chce tutaj tego zera ale mi każe
            double tmp1;
            commonRange = (sign1.calculatedValues.Length > sign2.calculatedValues.Length) ? sign1.calculatedValues.Length / 2 : sign2.calculatedValues.Length / 2;
            for(int i=0; i<commonRange-1; i++){
                //sign2.calculatedValues[i, 1] nie wiem czy to jest dobrze, nie jestem pewna czy rzeczywiscie tak sie wyciaga ta wartosc
                tmp=Math.Abs(sign1.calculatedValues[i, 1]-sign2.calculatedValues[i, 1]);
                tmp1=sign1.calculatedValues[i+1, 1]-sign2.calculatedValues[i+1, 1];
                if(tmp1>tmp){
                    tmp=tmp1;  
                }
            }
            MD=tmp;
            return MD;        
        }
    }
}
